#include<iostream>
#include<stdlib.h>
#include "server_config.h"
#include<proto.h>
#include<syslog.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/socket.h>
#include<signal.h>
#include<net/if.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include"thread_list.h"
#include"thread_channel.h"
#include"medialib.h"
#include<stdio.h>

using namespace std;
char errinfo[1024];

/*
命令行
-M 指定多播组地址
-P 指定端口
-F 前台运行
-D 指定媒体库的位置
-I 指定网卡设备
-H 帮助
*/
int server_fd;
sockaddr_in sendaddr; //对面的地址  
media_entity_st *list;
server_cfg_st server_cfg = {
    .recvport = RECVPORT,
    .mgroup = MULTIGROUP,
    .mediaDir = DEFAULT_MIDEI_DIR,
    .runmode = FORGROUND,   // 注意：默认前台运行
    .ifname = DEFAULT_IF //网卡名
};
void print_help(){
    cout<<"-M mutigroup"<<endl;
    cout<<"-P Prot"<<endl;
    cout<<"-F forground"<<endl;
    cout<<"-D MULTmedia dir"<<endl;
    cout<<"-I device"<<endl;
}

int daemonize(){
    int fd;
    pid_t pid = fork();
    if(pid<0){ // 出错报错，返回
        sprintf(errinfo,"fork() failed in file [%d] : line : [%d]",__FILE__,__LINE__);
        perror(errinfo);
        return -1;
    }
    if(pid>0){ // 父进程直接返回，并准备退出
        exit(0);
    }

    setsid();

    fd = open("/dev/null",O_RDWR);
    if(fd<0){
        syslog(LOG_WARNING,"/dev/null failed open");
        return -2;
    }
    dup2(fd,0);
    dup2(fd,1);
    dup2(fd,2);
    if(fd>2){
        close(fd);
    }

    chdir("/");
    umask(0);
    return 0;
}

void daemonexit(int s){

    thr_list_destory();
    thread_channel_destoryAll();
    media_freechannelList(list);

    
    syslog(LOG_WARNING,"signal %d catched",s);

    closelog();
    exit(0);

}

int socket_init(){
    server_fd = socket(AF_INET,SOCK_DGRAM,0);
    if(server_fd<0){
        syslog(LOG_ERR,"socket create failed");
        exit(1);
    }
    // 建立多播组
    ip_mreqn mreqn;
    inet_pton(AF_INET,server_cfg.mgroup,&mreqn.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreqn.imr_address);
    mreqn.imr_ifindex = if_nametoindex(server_cfg.ifname);
    // int optval=1;

    //注意：此处setsockopt后面两个参数不是布尔值，要参照中间两个参数
    //IP_MULTICAST_IF创建多播组
    if(setsockopt(server_fd,IPPROTO_IP,IP_MULTICAST_IF,&mreqn,sizeof(mreqn))<0){
        syslog(LOG_ERR,"setsockopt failed");
        exit(1);
    }
    // return server_fd;
    sendaddr.sin_family = AF_INET;
    sendaddr.sin_port = htons(atoi(server_cfg.recvport));
    inet_pton(AF_INET,server_cfg.mgroup,&sendaddr.sin_addr.s_addr); // 发送给的对象的地址是组播地址
    return 0;
}


int main(int argc,char* argv[]){

    //因为使用守护进程，所以不可能正常退出，所以设置信号
    //设置信号响应函数，使用sigaction而不是signal，因为signal有重入问题
    struct sigaction sa;
    sa.sa_handler = daemonexit;  //绑定信号处理函数

    sigemptyset(&sa.sa_mask); // 将信号集清空

    sigaddset(&sa.sa_mask,SIGTERM); // 将需要关注的信号加入信号集
    sigaddset(&sa.sa_mask,SIGINT); 
    sigaddset(&sa.sa_mask,SIGQUIT); 

    sigaction(SIGTERM,&sa,NULL); // NULL 表示对之前的状态不关心
    sigaction(SIGINT,&sa,NULL);
    sigaction(SIGQUIT,&sa,NULL);
    //当收到常用的结束信号，就直接调用对应的函数将该清理的内容清理


    //打开日志文件内容输出到log日志上
    openlog("netradio",LOG_PID|LOG_PERROR,LOG_DAEMON);//打开日志文件

    


    // 分析命令行
    char opt;
    if(argc>1){
        while(1){
            opt = getopt(argc,argv,"M:P:F:D:I:H");
            if(opt<0){
                // perror("getopt()");
                syslog(LOG_ERR,"fork() failed ");   // 需要重新学习日志的内容
                // 日志级别，日志信息,
                //不能写入 \n 因为syslogd会将 \n 当作字符写入
                exit(1);
            }
            switch(opt){
                case 'M':
                    if(optarg)
                        server_cfg.mgroup = optarg;
                    break;
                case 'P':
                    server_cfg.recvport = optarg;
                    break;
                case 'F':
                    server_cfg.runmode = (MODE)atoi(optarg);
                    break;
                case 'I':
                    server_cfg.ifname = optarg;
                    break;
                case 'H':
                    print_help();
                    exit(0);
                    break;
                case 'D':
                    server_cfg.mediaDir = optarg;
                    break;
                default:
                // cout<<"error"<<endl;
                syslog(LOG_ERR,"parameter error");
                abort();
                // break;
            };
        }
    }
    

    

// 做成守护进程
    if(server_cfg.runmode==DAEMON){
        daemonize(); // 设置守护进程
    }
    else if(server_cfg.runmode==FORGROUND){ 
        // 暂时什么也不做
    }else{
        // cout<<"mode error"<<endl;
        syslog(LOG_ERR,"mode error");
        exit(1);
    }



// 将输出重定向
// 调试的时候最好能够不是守护进程，方便
// socket初始化
    int server_fd;
    server_fd = socket_init();


// 获取频道信息，
    
    int list_len;
    int err = media_getchannelList(&list,&list_len);
    if(err){
        syslog(LOG_ERR,"meida- get channel failed");
        exit(1);
    }


// 节目单线程将读取到的频道信息发送，而对于每个频道具体音频内容
// 将使用频道线程单独发送，其中频道线程数和频道数量一致
// media lib 文件专门用于分析媒体库
// thread list 文件处理关于节目单的信息
// thread channel 处理每个频道的信息

// 



//创建节目单线程
    err = thr_list_create(list,list_len);
    if(err){
        syslog(LOG_ERR,"list create failed");
        exit(1);
    }

    //到目前为之，使用gdb调试存在两个线程正在usleep，一个是thr_list_create中调用的thr_list 
    //，还有一个是mytbf，每秒钟增加token，如果下面的循环一个一个来，则是一个线程一个线程的创建，下面的线程之间在
    //第一个的时候不应该互相影响
    //但是第一个线程

// 创建频道线程，n个线程对应n个频道
    int i=0;
    for(i=0;i<list_len;i++){
        err = thread_channel_create(list+i);
        usleep(1000);
        if(err){
            
        }
    }

    syslog(LOG_DEBUG,"%d channel threads created ",i);


    while(1){
	pause();
    }

// 关闭日志文件
    // closelog();



}
